#include <QTableWidgetSelectionRange>
#include <QAction>
#include <QTableWidgetItem>
#include <QApplication>
#include <QClipboard>
#include <QFile>
#include <QMessageBox>
#include <QFileDialog>
#include <QProgressDialog>
#include "spreadsheet.h"

#include "cell.h"
//#include <QDataStream>

const int SpreadSheet::mMaxRow = 999;
const int SpreadSheet::mMaxColumn = 26;
int SpreadSheet::doc_number_(1);

SpreadSheet::SpreadSheet(QWidget *parent) :
    QTableWidget(parent),
    pActionActive(new QAction(this)),
    mIsSaved(false),
    mAutoRecalculated(false)
{
    setActionActive();

    setItemPrototype(new Cell());

    connect(this, static_cast<void (SpreadSheet::*)(QTableWidgetItem*)>(&SpreadSheet::itemChanged),
            this, static_cast<void (SpreadSheet::*)()>(&SpreadSheet::somethingChanged));

    clear();
}

SpreadSheet* SpreadSheet::newFile(QWidget* widget)
{
    SpreadSheet* spread(new SpreadSheet(widget));

    // 设置spread属性
    spread->mCurFile = tr("excel %1").arg(doc_number_);
    spread->pActionActive->setText(spread->mCurFile);
    spread->setWindowTitle(spread->mCurFile);
    spread->setFileAttribute(spread, spread->mCurFile);

    ++doc_number_;

    return spread;
}

SpreadSheet* SpreadSheet::open(QWidget* parent)
{
    SpreadSheet* sheet(nullptr);

    QString file_path = QFileDialog::getOpenFileName(parent, tr("Open a file."), ".");

    if ( file_path.isEmpty()) {
        sheet = nullptr;
    } else {
        sheet = new SpreadSheet(parent);

        if ( sheet->openFile(sheet, file_path))  {
            sheet->setFileAttribute(sheet, file_path);

            return sheet;
        } else {
            delete sheet;

            return sheet = nullptr;
        }
    }

    return sheet;
}

bool SpreadSheet::autoRecalculate() const
{
    return mAutoRecalculated;
}

void SpreadSheet::setAutoRecalculate(bool recalculateFlag)
{
    mAutoRecalculated = recalculateFlag;

    if ( mAutoRecalculated) {
        recalculate();
    }
}

QString SpreadSheet::currentLocation() const
{
    return (QString::number(currentRow() + 1) + QChar(currentColumn() + 'A'));
}

QString SpreadSheet::currentFormula() const
{
    return "";
}

QAction* SpreadSheet::windowMenuAction() const
{
    return pActionActive;
}

QTableWidgetSelectionRange SpreadSheet::selectedRange() const
{
    QList<QTableWidgetSelectionRange> ranges = selectedRanges();
    //QTableWidgetSelectionRange* r(0);

    if ( ranges.isEmpty()) {
        return QTableWidgetSelectionRange();
    } else {
        //return the first data in list.
        return ranges.first();
    }
}

void SpreadSheet::clear()
{
    setRowCount(0);
    setColumnCount(0);
    setRowCount(SpreadSheet::mMaxRow);
    setColumnCount(SpreadSheet::mMaxColumn);

    QTableWidgetItem* item(0);

    for (int i(0); i != SpreadSheet::mMaxColumn; ++i) {
        item = new QTableWidgetItem();

        item->setText(QString(QChar('A' + i)));
        setHorizontalHeaderItem(i, item);
    }
}

void SpreadSheet::recalculate()
{
    for (int i(0); i != SpreadSheet::mMaxRow; ++i) {
        for (int j(0); j != SpreadSheet::mMaxColumn; ++j) {
            if ( cell(i, j)) {
                cell(i, j)->setDirty();
            }
        }
    }

    viewport()->update();
}

void SpreadSheet::cut()
{
    copy();
    del();
}

void SpreadSheet::del()
{
    QList<QTableWidgetItem*> items = selectedItems();

    if ( !items.isEmpty()) {
        foreach (QTableWidgetItem* item, items) {
           delete item;
        }
    }
}

void SpreadSheet::setFileAttribute(SpreadSheet *sheet, const QString &filePath)
{
    if ( !filePath.isEmpty()) {
        sheet->mCurFile = filePath;
        QString fileName = sheet->strippedName(filePath);
        sheet->setWindowTitle(fileName);
        sheet->pActionActive->setText(fileName);
    }
}

void SpreadSheet::copy()
{
    QTableWidgetSelectionRange range = selectedRange();

    QString strContent;

    for (int i(0); i != range.rowCount(); ++i) {
        if (0 < i) {
           strContent.append('\n');
        }

        for (int j(0); j != range.columnCount(); ++j) {
            if (0 < j) {
                strContent.append('\t');
            }

            strContent.append(formula(range.topRow() + i, range.leftColumn() +j));
        }
    }

    QApplication::clipboard()->setText(strContent);
}

void SpreadSheet::save()
{
    QFile file(mCurFile);
    if (file.exists()) {
        QFileInfo fileInfo(file);
        writeFile(fileInfo.absoluteFilePath());
    } else {
        writeFile(QDir::currentPath() + "/" + mCurFile);
    }
}

bool SpreadSheet::saveAs()
{
    bool flag(false);
    QString filePath =
            QFileDialog::getSaveFileName(this,
                                         tr("Save the file"),
                                         tr("."));

    if ( !filePath.isEmpty()) {
        flag = writeFile(filePath);
    } else {
        flag = false;
    }

    return flag;
}

SpreadSheet* SpreadSheet::openFile(SpreadSheet *sheet, const QString filePath)
{
    if ( sheet->readFile(filePath)) {
        setActionActive();
        SpreadSheet::strippedName(filePath);

        return sheet;
    } else {
        return 0;
    }
}

QString SpreadSheet::text(int row, int column) const
{
    QString str;
    Cell* c = cell(row, column);

    if ( c) {
        str.append(c->text());
    }

    return str;
}

QString SpreadSheet::formula(int row, int column) const
{
    Cell* c = cell(row, column);
    QString str;

    if ( c) {
        str = c->formula();
    } else {
        str = "";
    }

    return str;
}

void SpreadSheet::setFormula(int row, int column, const QString &str)
{
    Cell* c = cell(row, column);

    if ( !c) {
        c = new Cell();
        setItem(row, column, c);
    }

    c->setFormula(str);
}

Cell* SpreadSheet::cell(int row, int column) const
{
    return static_cast<Cell*>(item(row, column));
}

void SpreadSheet::selectCurrentRow()
{
    selectRow(currentRow());
}

void SpreadSheet::selectCurrentColumn()
{
    selectColumn(currentColumn());
}

void SpreadSheet::somethingChanged()
{
    if ( mAutoRecalculated) {
        recalculate();
    }

    //emit modified();
}

void SpreadSheet::setActionActive()
{
    pActionActive->setCheckable(true);

    connect(pActionActive, &QAction::triggered,
            this, static_cast<void (SpreadSheet::*)()>(&SpreadSheet::setFocus));
    connect(pActionActive, &QAction::triggered,
            this, &SpreadSheet::show);
}

bool SpreadSheet::readFile(const QString &filePath)
{
    QFile file(filePath);

    if ( !file.open(QIODevice::ReadOnly)) {
        QMessageBox::warning(this, tr("Spreadsheet"),
                             tr("Cannot read file %1:\n%2.")
                             .arg(file.fileName())
                             .arg(file.errorString()));

        return false;
    }

    QDataStream in(&file);
    in.setVersion(QDataStream::Qt_5_3);

    quint64 magic;
    in >> magic;

    if (SpreadSheet::MagicNumber != magic) {
        QMessageBox::warning(this, tr("Spreadsheet"),
                             tr("The file is not a Spreadsheet file."));

        return false;
    }

    clear();

    quint32 row;
    quint32 column;
    QString str;

    QProgressDialog* process =
            progressDialog(this,
                           tr("Load %1").arg(file.fileName()),
                           SpreadSheet::mMaxRow);
    process->setModal(true);

    QApplication::setOverrideCursor(Qt::WaitCursor);

    while ( !in.atEnd()) {
        in >> row >> column >> str;
        setFormula(row, column, str);

        process->setValue(row);

        if ( process->wasCanceled()) {
            clear();
            delete process;
            file.close();
        }
    }

    QApplication::restoreOverrideCursor();
    delete process;

    return true;
}

bool SpreadSheet::writeFile(const QString &filePath)
{
    QFile file(filePath);

    if ( !file.open(QIODevice::WriteOnly)) {
        QMessageBox::warning(this, tr("Spreadsheet"),
                             tr("Cannot write file %1:\n%2.")
                             .arg(file.fileName())
                             .arg(file.errorString()));

        return false;
    }

    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_5_3);

    out<< (quint64)SpreadSheet::MagicNumber;

    QProgressDialog* progress =
            progressDialog(this, tr("Save %1").arg(file.fileName()), SpreadSheet::mMaxRow);
    progress->setModal(true);

    QApplication::setOverrideCursor(Qt::WaitCursor);
    QString str;

    for (int i(0); i != SpreadSheet::mMaxRow; ++i) {
        progress->setValue(i);
        qApp->processEvents(QEventLoop::ExcludeUserInputEvents);

        if ( progress->wasCanceled()) {
            file.remove();
            delete progress;
            return false;
        }

        for (int j(0); j != SpreadSheet::mMaxColumn; ++j) {
            str = formula(i, j);

            if ( !str.isEmpty()) {
                out << (quint32)i << (quint32)j << str;
            }
        }
    }

    delete progress;
    QApplication::restoreOverrideCursor();

    return true;
}

QString SpreadSheet::strippedName(const QString &filePath)
{
    return QFileInfo(filePath).fileName();
}

QProgressDialog* SpreadSheet::progressDialog(QWidget* widget, const QString &str, const int range)
{
     QProgressDialog* progressDialog(new QProgressDialog(widget));
     progressDialog->setLabelText(str);
     progressDialog->setRange(0, range);

     return progressDialog;
}
